/**
 * Copyright 2009 Emory University
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated  documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 
 * and/or sell copies of the Software, and to permit persons to whom the 
 * Software is furnished to do so, subject to the following conditions: The 
 * above copyright notice and this permission notice shall be included in all 
 * copies or substantial portions of the Software. 
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */
package com.markgrand.structuredSql

/**
 * Trait to supply logic for quoting SQL IDs when necessary and for explicitly 
 * quoting SQL IDs.
 */
private[structuredSql] trait SQLIdentifier {
  private val unquotedIdentifierRegex = "\\A[_\\p{L}][_\\p{L}\\p{Nd}]*\\z".r
  
  /**
   * Append a quoted SQL identifier based on the given string to the given 
   * StringBuilder. Any double quote characters (") are doubled and the 
   * resultng string is surrounded by double quote characters.
   * 
   * @param s
   *    The string to base the quoted identifier on.
   * @param builder
   *    The String builder to append to.
   */
  protected def appendQuotedIdentifier(s:String, builder:StringBuilder) {
     builder.append("\"")
     for (i <- 0 to s.length-1) {
       s.charAt(i) match {
         case '\"' => builder.append("\"").append("\"")
         case c => builder.append(c)
       }
     }
     builder.append("\"")
  }
  
  /**
   * Append an SQL identifier based on the given string to the given 
   * StringBuilder.  If the string does not fit the syntax required for an 
   * unquoted SQL identifier, then it is lowercased and returned as a quoted 
   * identifier.
   * <p>
   * TODO add logic to lowercase or uppercase as is appropriate for the database being used.
   * 
   * @param s
   *    The string to base the identifier on.
   * @param builder
   *    The String builder to append to.
   */
  protected def appendIdentifier(s:String, builder:StringBuilder) {
    if (unquotedIdentifierRegex.findFirstMatchIn(s).isDefined) {
      // OK to use as is
      builder.append(s)
    } else {
      appendQuotedIdentifier(s.toLowerCase(), builder)
    }
  }
  
  /**
   * Append strings to the given StringBilder as SQL identifiers
   * separated by the given separator.
   * 
   * @param strings
   *    The strings to be appended as SQL identifiers.
   * @param builder
   *    The StringBuilder object to append to.
   * @param separator
   *    The separator to append between identifiers.
   */
  protected def appendIdentifierSeq(strings:Seq[String], builder:StringBuilder, separator:String) {
    val iterator = strings.iterator
    if (iterator.hasNext) {
      appendIdentifier(iterator.next, builder)
      while (iterator.hasNext) {
        builder.append(separator)
        appendIdentifier(iterator.next, builder)
      }
    }
  }
  
  /**
   * Append strings to the given StringBilder as SQL identifiers
   * separated commas.
   * 
   * @param strings
   *    The strings to be appended as SQL identifiers.
   * @param builder
   *    The StringBuilder object to append to.
   */
  protected def appendIdentifierSeq(strings:Seq[String], builder:StringBuilder) {
    appendIdentifierSeq(strings, builder, ",")
  }
}
